home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / ui4 / systemtray.py < prev    next >
Text File  |  2009-10-09  |  27KB  |  728 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20.  
  21. # Std Lib
  22. import sys
  23. import struct
  24. import select
  25. import os
  26. import signal
  27. import os.path
  28. import time
  29.  
  30.  
  31. # Local
  32. from base.g import *
  33. from base import device, utils, models
  34. from base.codes import *
  35. from ui_utils import *
  36.  
  37. # PyQt
  38. try:
  39.     from PyQt4.QtCore import *
  40.     from PyQt4.QtGui import *
  41. except ImportError:
  42.     log.error("Python bindings for Qt4 not found. Try using --qt3. Exiting!")
  43.     sys.exit(1)
  44.  
  45. from systrayframe import SystrayFrame
  46.  
  47. # dbus (required)
  48. try:
  49.     import dbus
  50.     #import dbus.service
  51.     from dbus import SessionBus, lowlevel
  52.     #from dbus.mainloop.qt import DBusQtMainLoop
  53. except ImportError:
  54.     log.error("Python bindings for dbus not found. Exiting!")
  55.     sys.exit(1)
  56.  
  57. import warnings
  58. # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
  59. # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
  60. warnings.simplefilter("ignore", DeprecationWarning)
  61.  
  62.  
  63. # pynotify (optional)
  64. have_pynotify = True
  65. try:
  66.     import pynotify
  67. except ImportError:
  68.     have_pynotify = False
  69.  
  70.  
  71. TRAY_MESSAGE_DELAY = 10000
  72. HIDE_INACTIVE_DELAY = 5000
  73. BLIP_DELAY = 2000
  74. SET_MENU_DELAY = 1000
  75. MAX_MENU_EVENTS = 10
  76.  
  77. ERROR_STATE_TO_ICON = {
  78.     ERROR_STATE_CLEAR:        QSystemTrayIcon.Information,
  79.     ERROR_STATE_OK:           QSystemTrayIcon.Information,
  80.     ERROR_STATE_WARNING:      QSystemTrayIcon.Warning,
  81.     ERROR_STATE_ERROR:        QSystemTrayIcon.Critical,
  82.     ERROR_STATE_LOW_SUPPLIES: QSystemTrayIcon.Warning,
  83.     ERROR_STATE_BUSY:         QSystemTrayIcon.Warning,
  84.     ERROR_STATE_LOW_PAPER:    QSystemTrayIcon.Warning,
  85.     ERROR_STATE_PRINTING:     QSystemTrayIcon.Information,
  86.     ERROR_STATE_SCANNING:     QSystemTrayIcon.Information,
  87.     ERROR_STATE_PHOTOCARD:    QSystemTrayIcon.Information,
  88.     ERROR_STATE_FAXING:       QSystemTrayIcon.Information,
  89.     ERROR_STATE_COPYING:      QSystemTrayIcon.Information,
  90. }
  91.  
  92. if have_pynotify:
  93.     info = getPynotifyIcon('info')
  94.     warn = getPynotifyIcon('warning')
  95.     err = getPynotifyIcon('error')
  96.     ERROR_STATE_TO_ICON_AND_URGENCY_PYNOTIFY = {
  97.         ERROR_STATE_CLEAR:        (info, pynotify.URGENCY_LOW),
  98.         ERROR_STATE_OK:           (info, pynotify.URGENCY_LOW),
  99.         ERROR_STATE_WARNING:      (warn, pynotify.URGENCY_NORMAL),
  100.         ERROR_STATE_ERROR:        (err, pynotify.URGENCY_CRITICAL),
  101.         ERROR_STATE_LOW_SUPPLIES: (warn, pynotify.URGENCY_NORMAL),
  102.         ERROR_STATE_BUSY:         (warn, pynotify.URGENCY_NORMAL),
  103.         ERROR_STATE_LOW_PAPER:    (warn, pynotify.URGENCY_NORMAL),
  104.         ERROR_STATE_PRINTING:     (info, pynotify.URGENCY_LOW),
  105.         ERROR_STATE_SCANNING:     (info, pynotify.URGENCY_LOW),
  106.         ERROR_STATE_PHOTOCARD:    (info, pynotify.URGENCY_LOW),
  107.         ERROR_STATE_FAXING:       (info, pynotify.URGENCY_LOW),
  108.         ERROR_STATE_COPYING:      (info, pynotify.URGENCY_LOW),
  109.     }
  110.  
  111. devices = {} # { <device_uri> : HistoryDevice(), ... }
  112.  
  113.  
  114. class DeviceMenu(QMenu):
  115.     def __init__(self, title, parent, device_uri, device_hist, index):
  116.         QMenu.__init__(self, title, parent)
  117.         self.device_uri = device_uri
  118.         self.device_hist = device_hist
  119.         self.index = index
  120.  
  121.  
  122.     def update(self):
  123.         self.clear()
  124.  
  125.         if self.device_hist:
  126.             first = True
  127.             for e in self.device_hist:
  128.                 error_state = STATUS_TO_ERROR_STATE_MAP.get(e.event_code, ERROR_STATE_CLEAR)
  129.                 ess = device.queryString(e.event_code, 0)
  130.  
  131.                 a = QAction(QIcon(getStatusListIcon(error_state)[self.index]),
  132.                                     QString("%1 %2").arg(ess).arg(getTimeDeltaDesc(e.timedate)), self)
  133.  
  134.                 if first:
  135.                     f = a.font()
  136.                     f.setBold(True)
  137.                     a.setFont(f)
  138.                     self.setIcon(QIcon(getStatusListIcon(error_state)[self.index]))
  139.                     first = False
  140.  
  141.                 self.addAction(a)
  142.  
  143.         else:
  144.             self.addAction(QIcon(load_pixmap("warning", "16x16")),
  145.                 QApplication.translate("SystemTray", "(No events)", None, QApplication.UnicodeUTF8))
  146.  
  147.  
  148.  
  149. class HistoryDevice(QObject):
  150.     def __init__(self, device_uri, needs_update=True):
  151.         self.needs_update = needs_update
  152.         self.device_uri = device_uri
  153.  
  154.         back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
  155.                 device.parseDeviceURI(device_uri)
  156.  
  157.         if bus == 'usb':
  158.             self.id = serial
  159.         elif bus == 'net':
  160.             self.id = host
  161.         elif bus == 'par':
  162.             self.id = dev_file
  163.         else:
  164.             self.id = 'unknown'
  165.  
  166.         self.model = models.normalizeModelUIName(model)
  167.  
  168.         if back_end == 'hp':
  169.             self.device_type = DEVICE_TYPE_PRINTER
  170.             self.menu_text = self.__tr("%1 Printer (%2)").arg(self.model).arg(self.id)
  171.  
  172.         elif back_end == 'hpaio':
  173.             self.device_type = DEVICE_TYPE_SCANNER
  174.             self.menu_text = self.__tr("%1 Scanner (%2)").arg(self.model).arg(self.id)
  175.  
  176.         elif back_end == 'hpfax':
  177.             self.device_type = DEVICE_TYPE_FAX
  178.             self.menu_text = self.__tr("%1 Fax (%2)").arg(self.model).arg(self.id)
  179.  
  180.         else:
  181.             self.device_type = DEVICE_TYPE_UNKNOWN
  182.             self.menu_text = self.__tr("%1 (%2)").arg(self.model).arg(self.id)
  183.  
  184.         self.mq = device.queryModelByURI(self.device_uri)
  185.         self.index = 0
  186.         if self.mq.get('tech-type', TECH_TYPE_NONE) in (TECH_TYPE_MONO_LASER, TECH_TYPE_COLOR_LASER):
  187.             self.index = 1
  188.         self.history = None
  189.  
  190.  
  191.     def getHistory(self, service):
  192.         if service is not None and self.needs_update:
  193.             device_uri, h = service.GetHistory(self.device_uri)
  194.             self.history = [device.Event(*tuple(e)) for e in list(h)[:-MAX_MENU_EVENTS:-1]]
  195.             self.needs_update = False
  196.  
  197.  
  198.     def __tr(self, s, c=None):
  199.         return QApplication.translate("SystemTray", s, c, QApplication.UnicodeUTF8)
  200.  
  201.  
  202.  
  203.  
  204. class SystraySettingsDialog(QDialog):
  205.     def __init__(self, parent, systray_visible, polling,
  206.                  polling_interval, systray_messages,
  207.                  device_list=None):
  208.  
  209.         QDialog.__init__(self, parent)
  210.  
  211.         self.systray_visible = systray_visible
  212.         self.systray_messages = systray_messages
  213.  
  214.         if device_list is not None:
  215.             self.device_list = device_list
  216.         else:
  217.             self.device_list = {}
  218.  
  219.         self.polling = polling
  220.         self.polling_interval = polling_interval
  221.  
  222.         self.initUi()
  223.         self.SystemTraySettings.updateUi()
  224.  
  225.  
  226.     def initUi(self):
  227.         self.setObjectName("SystraySettingsDialog")
  228.         self.resize(QSize(QRect(0,0,488,565).size()).expandedTo(self.minimumSizeHint()))
  229.  
  230.         self.gridlayout = QGridLayout(self)
  231.         self.gridlayout.setObjectName("gridlayout")
  232.  
  233.         self.SystemTraySettings = SystrayFrame(self)
  234.         self.SystemTraySettings.initUi(self.systray_visible,
  235.                                        self.polling, self.polling_interval,
  236.                                        self.device_list,
  237.                                        self.systray_messages)
  238.  
  239.         sizePolicy = QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
  240.         sizePolicy.setHorizontalStretch(0)
  241.         sizePolicy.setVerticalStretch(0)
  242.         sizePolicy.setHeightForWidth(self.SystemTraySettings.sizePolicy().hasHeightForWidth())
  243.         self.SystemTraySettings.setSizePolicy(sizePolicy)
  244.         self.SystemTraySettings.setFrameShadow(QFrame.Raised)
  245.         self.SystemTraySettings.setObjectName("SystemTraySettings")
  246.         self.gridlayout.addWidget(self.SystemTraySettings,0,0,1,2)
  247.  
  248.         spacerItem = QSpacerItem(301,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
  249.         self.gridlayout.addItem(spacerItem,1,0,1,1)
  250.  
  251.         self.StdButtons = QDialogButtonBox(self)
  252.         self.StdButtons.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.NoButton|QDialogButtonBox.Ok)
  253.         self.StdButtons.setCenterButtons(False)
  254.         self.StdButtons.setObjectName("StdButtons")
  255.         self.gridlayout.addWidget(self.StdButtons,1,1,1,1)
  256.  
  257.         QObject.connect(self.StdButtons, SIGNAL("accepted()"), self.acceptClicked)
  258.         QObject.connect(self.StdButtons, SIGNAL("rejected()"), self.reject)
  259.         #QMetaObject.connectSlotsByName(self)
  260.  
  261.         self.setWindowTitle(self.__tr("HP Device Manager - System Tray Settings"))
  262.  
  263.  
  264.     def acceptClicked(self):
  265.         self.systray_visible = self.SystemTraySettings.systray_visible
  266.         self.polling = self.SystemTraySettings.polling
  267.         self.polling_interval = self.SystemTraySettings.polling_interval
  268.         self.device_list = self.SystemTraySettings.device_list
  269.         self.systray_messages = self.SystemTraySettings.systray_messages
  270.         self.accept()
  271.  
  272.  
  273.     def __tr(self, s, c=None):
  274.         return QApplication.translate("SystraySettingsDialog", s, c, QApplication.UnicodeUTF8)
  275.  
  276.  
  277.  
  278. class SystemTrayApp(QApplication):
  279.     def __init__(self, args, read_pipe):
  280.         QApplication.__init__(self, args)
  281.  
  282.         self.menu = None
  283.         self.read_pipe = read_pipe
  284.         self.fmt = "64s64sI32sI64sf"
  285.         self.fmt_size = struct.calcsize(self.fmt)
  286.         self.timer_active = False
  287.         self.active_icon = False
  288.         self.user_settings = UserSettings()
  289.         self.user_settings.load()
  290.         self.user_settings.debug()
  291.  
  292.         self.tray_icon = QSystemTrayIcon()
  293.  
  294.         pm = load_pixmap("hp_logo", "32x32")
  295.         self.prop_icon = QIcon(pm)
  296.  
  297.         a = load_pixmap('active', '16x16')
  298.         painter = QPainter(pm)
  299.         painter.drawPixmap(32, 0, a)
  300.         painter.end()
  301.  
  302.         self.prop_active_icon = QIcon(pm)
  303.  
  304.         self.tray_icon.setIcon(self.prop_icon)
  305.  
  306.         self.session_bus = SessionBus()
  307.         self.service = None
  308.  
  309.         for d in device.getSupportedCUPSDevices(back_end_filter=['hp', 'hpfax']):
  310.             self.addDevice(d)
  311.  
  312.         self.tray_icon.setToolTip(self.__tr("HPLIP Status Service"))
  313.         QObject.connect(self.tray_icon, SIGNAL("messageClicked()"), self.messageClicked)
  314.         notifier = QSocketNotifier(self.read_pipe, QSocketNotifier.Read)
  315.         QObject.connect(notifier, SIGNAL("activated(int)"), self.notifierActivated)
  316.         QObject.connect(self.tray_icon, SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.trayActivated)
  317.         self.tray_icon.show()
  318.  
  319.         if self.user_settings.systray_visible == SYSTRAY_VISIBLE_SHOW_ALWAYS:
  320.             self.tray_icon.setVisible(True)
  321.         else:
  322.             QTimer.singleShot(HIDE_INACTIVE_DELAY, self.timeoutHideWhenInactive) # show icon for awhile @ startup
  323.  
  324.         self.tray_icon.setIcon(self.prop_active_icon)
  325.         self.active_icon = True
  326.  
  327.         QTimer.singleShot(SET_MENU_DELAY, self.initDone)
  328.  
  329.  
  330.     def initDone(self):
  331.         self.tray_icon.setIcon(self.prop_icon)
  332.         self.active_icon = False
  333.  
  334.         self.setMenu()
  335.  
  336.  
  337.     def addDevice(self, device_uri):
  338.         try:
  339.             devices[device_uri]
  340.         except KeyError:
  341.             devices[device_uri] = HistoryDevice(device_uri)
  342.         else:
  343.             devices[device_uri].needs_update = True
  344.  
  345.  
  346.  
  347.     def setMenu(self):
  348.         self.menu = QMenu()
  349.  
  350.         title = QWidgetAction(self.menu)
  351.         #title.setDisabled(True)
  352.  
  353.         hbox = QFrame(self.menu)
  354.         layout = QHBoxLayout(hbox)
  355.         layout.setMargin(3)
  356.         layout.setSpacing(5)
  357.         pix_label = QLabel(hbox)
  358.  
  359.         layout.insertWidget(-1, pix_label, 0)
  360.  
  361.         icon_size = self.menu.style().pixelMetric(QStyle.PM_SmallIconSize)
  362.         pix_label.setPixmap(self.prop_icon.pixmap(icon_size))
  363.  
  364.         label = QLabel(hbox)
  365.         layout.insertWidget(-1, label, 20)
  366.         title.setDefaultWidget(hbox)
  367.  
  368.         label.setText(self.__tr("HPLIP Status Service"))
  369.  
  370.         f = label.font()
  371.         f.setBold(True)
  372.         label.setFont(f)
  373.         self.menu.insertAction(None, title)
  374.  
  375.         if devices:
  376.             if self.service is None:
  377.                 t = 0
  378.                 while t < 3:
  379.                     try:
  380.                         self.service = self.session_bus.get_object('com.hplip.StatusService',
  381.                                                                   "/com/hplip/StatusService")
  382.                     except DBusException:
  383.                         log.warn("Unable to connect to StatusService. Retrying...")
  384.  
  385.                     t += 1
  386.                     time.sleep(0.5)
  387.  
  388.             if self.service is not None:
  389.                 self.menu.addSeparator()
  390.  
  391.                 for d in devices:
  392.                     devices[d].getHistory(self.service)
  393.  
  394.                     menu = DeviceMenu(devices[d].menu_text, self.menu, d, devices[d].history, devices[d].index)
  395.                     self.menu.addMenu(menu)
  396.                     menu.update()
  397.  
  398.  
  399.         self.menu.addSeparator()
  400.         self.menu.addAction(self.__tr("HP Device Manager..."), self.toolboxTriggered)
  401.  
  402.         self.menu.addSeparator()
  403.  
  404.         self.settings_action = self.menu.addAction(QIcon(load_pixmap('settings', '16x16')),
  405.                                     self.__tr("Settings..."),  self.settingsTriggered)
  406.  
  407.         self.menu.addSeparator()
  408.         self.menu.addAction(QIcon(load_pixmap('quit', '16x16')), "Quit", self.quitTriggered)
  409.         self.tray_icon.setContextMenu(self.menu)
  410.  
  411.  
  412.  
  413.  
  414.     def settingsTriggered(self):
  415.         if self.menu is None:
  416.             return
  417.  
  418.         self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
  419.         try:
  420.             dlg = SystraySettingsDialog(self.menu, self.user_settings.systray_visible,
  421.                                         self.user_settings.polling, self.user_settings.polling_interval,
  422.                                         self.user_settings.systray_messages,
  423.                                         self.user_settings.polling_device_list)
  424.  
  425.             if dlg.exec_() == QDialog.Accepted:
  426.                 self.user_settings.systray_visible = dlg.systray_visible
  427.                 self.user_settings.systray_messages = dlg.systray_messages
  428.  
  429.                 self.user_settings.save()
  430.  
  431.                 if self.user_settings.systray_visible == SYSTRAY_VISIBLE_SHOW_ALWAYS:
  432.                     log.debug("Showing...")
  433.                     self.tray_icon.setVisible(True)
  434.  
  435.                 else:
  436.                     log.debug("Waiting to hide...")
  437.                     QTimer.singleShot(HIDE_INACTIVE_DELAY, self.timeoutHideWhenInactive)
  438.  
  439.                 self.sendMessage('', '', EVENT_USER_CONFIGURATION_CHANGED)
  440.  
  441.         finally:
  442.             self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
  443.  
  444.  
  445.     def timeoutHideWhenInactive(self):
  446.         log.debug("Hiding...")
  447.         if self.user_settings.systray_visible in (SYSTRAY_VISIBLE_HIDE_WHEN_INACTIVE, SYSTRAY_VISIBLE_HIDE_ALWAYS):
  448.             self.tray_icon.setVisible(False)
  449.             log.debug("Hidden")
  450.  
  451.  
  452.     def updateMenu(self):
  453.         if self.menu is None:
  454.             return
  455.         for a in self.menu.actions():
  456.             try:
  457.                 a.menu().update()
  458.             except AttributeError:
  459.                 continue
  460.  
  461.  
  462.  
  463.     def trayActivated(self, reason):
  464.         if reason == QSystemTrayIcon.Context:
  465.             self.updateMenu()
  466.  
  467.  
  468.         elif reason == QSystemTrayIcon.DoubleClick:
  469.             #print "double click"
  470.             self.toolboxTriggered()
  471.             pass
  472.  
  473.         elif reason == QSystemTrayIcon.Trigger:
  474.             #print "single click"
  475.             pass
  476.  
  477.         elif reason == QSystemTrayIcon.MiddleClick:
  478.             #print "middle click"
  479.             pass
  480.  
  481.  
  482.     def messageClicked(self):
  483.         #print "\nPARENT: message clicked"
  484.         pass
  485.  
  486.  
  487.     def quitTriggered(self):
  488.         log.debug("Exiting")
  489.         self.sendMessage('', '', EVENT_SYSTEMTRAY_EXIT)
  490.         self.quit()
  491.  
  492.  
  493.     def toolboxTriggered(self):
  494.         try:
  495.             os.waitpid(-1, os.WNOHANG)
  496.         except OSError:
  497.             pass
  498.  
  499.         # See if it is already running...
  500.         ok, lock_file = utils.lock_app('hp-toolbox', True)
  501.  
  502.         if ok: # able to lock, not running...
  503.             utils.unlock(lock_file)
  504.  
  505.             path = utils.which('hp-toolbox')
  506.             if path:
  507.                 path = os.path.join(path, 'hp-toolbox')
  508.             else:
  509.                 self.tray_icon.showMessage(self.__tr("HPLIP Status Service"),
  510.                                 self.__tr("Unable to locate hp-toolbox on system PATH."),
  511.                                 QSystemTrayIcon.Critical, TRAY_MESSAGE_DELAY)
  512.  
  513.                 log.error("Unable to find hp-toolbox on PATH.")
  514.                 return
  515.  
  516.             #log.debug(path)
  517.             log.debug("Running hp-toolbox: hp-toolbox")
  518.             os.spawnlp(os.P_NOWAIT, path, 'hp-toolbox')
  519.  
  520.         else: # ...already running, raise it
  521.             self.sendMessage('', '', EVENT_RAISE_DEVICE_MANAGER, interface='com.hplip.Toolbox')
  522.  
  523.  
  524.     def sendMessage(self, device_uri, printer_name, event_code, username=prop.username,
  525.                     job_id=0, title='', pipe_name='', interface='com.hplip.StatusService'):
  526.         #device.Event(device_uri, printer_name, event_code, username, job_id, title).send_via_dbus(SessionBus(), interface)
  527.         device.Event(device_uri, printer_name, event_code, username, job_id, title).send_via_dbus(self.session_bus, interface)
  528.  
  529.  
  530.     def notifierActivated(self, s):
  531.         m = ''
  532.         while True:
  533.             try:
  534.                 r, w, e = select.select([self.read_pipe], [], [self.read_pipe], 1.0)
  535.             except select.error:
  536.                 log.debug("Error in select()")
  537.                 break
  538.  
  539.             if e:
  540.                 log.error("Pipe error: %s" % e)
  541.                 break
  542.  
  543.             if r:
  544.                 m = ''.join([m, os.read(self.read_pipe, self.fmt_size)])
  545.                 while len(m) >= self.fmt_size:
  546.                     event = device.Event(*struct.unpack(self.fmt, m[:self.fmt_size]))
  547.  
  548.                     m = m[self.fmt_size:]
  549.  
  550.                     if event.event_code == EVENT_USER_CONFIGURATION_CHANGED:
  551.                         log.debug("Re-reading configuration (EVENT_USER_CONFIGURATION_CHANGED)")
  552.                         self.user_settings.load()
  553.                         self.user_settings.debug()
  554.  
  555.                     elif event.event_code == EVENT_SYSTEMTRAY_EXIT:
  556.                         self.quit()
  557.                         return
  558.  
  559.                     if self.user_settings.systray_visible in \
  560.                         (SYSTRAY_VISIBLE_SHOW_ALWAYS, SYSTRAY_VISIBLE_HIDE_WHEN_INACTIVE):
  561.  
  562.                         log.debug("Showing...")
  563.                         self.tray_icon.setVisible(True)
  564.  
  565.                         if event.event_code == EVENT_DEVICE_UPDATE_ACTIVE:
  566.                             if not self.active_icon:
  567.                                 self.tray_icon.setIcon(self.prop_active_icon)
  568.                                 self.active_icon = True
  569.                             continue
  570.  
  571.                         elif event.event_code == EVENT_DEVICE_UPDATE_INACTIVE:
  572.                             if self.active_icon:
  573.                                 self.tray_icon.setIcon(self.prop_icon)
  574.                                 self.active_icon = False
  575.                             continue
  576.  
  577.                         elif event.event_code == EVENT_DEVICE_UPDATE_BLIP:
  578.                             if not self.active_icon:
  579.                                 self.tray_icon.setIcon(self.prop_active_icon)
  580.                                 self.active_icon = True
  581.                                 QTimer.singleShot(BLIP_DELAY, self.blipTimeout)
  582.                             continue
  583.  
  584.                     if self.user_settings.systray_visible in (SYSTRAY_VISIBLE_HIDE_WHEN_INACTIVE, SYSTRAY_VISIBLE_HIDE_ALWAYS):
  585.                         log.debug("Waiting to hide...")
  586.                         QTimer.singleShot(HIDE_INACTIVE_DELAY, self.timeoutHideWhenInactive)
  587.  
  588.                     if event.event_code <= EVENT_MAX_USER_EVENT:
  589.                         self.addDevice(event.device_uri)
  590.                         self.setMenu()
  591.  
  592.                         if self.tray_icon.supportsMessages():
  593.  
  594.                             log.debug("Tray icon message:")
  595.                             event.debug()
  596.  
  597.                             error_state = STATUS_TO_ERROR_STATE_MAP.get(event.event_code, ERROR_STATE_CLEAR)
  598.                             desc = device.queryString(event.event_code)
  599.  
  600.                             show_message = False
  601.                             if self.user_settings.systray_messages == SYSTRAY_MESSAGES_SHOW_ALL: # OK, Busy
  602.                                 show_message = True
  603.  
  604.                             elif self.user_settings.systray_messages in (SYSTRAY_MESSAGES_SHOW_ERRORS_AND_WARNINGS, SYSTRAY_MESSAGES_SHOW_ERRORS_ONLY):
  605.                                 if error_state == ERROR_STATE_ERROR:
  606.                                     show_message = True
  607.  
  608.                                 elif self.user_settings.systray_messages == SYSTRAY_MESSAGES_SHOW_ERRORS_AND_WARNINGS and \
  609.                                     error_state in (ERROR_STATE_WARNING, ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
  610.  
  611.                                     show_message = True
  612.  
  613.                             if event.printer_name:
  614.                                 d = QString(event.printer_name)
  615.                             else:
  616.                                 back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
  617.                                                 device.parseDeviceURI(event.device_uri)
  618.  
  619.                                 if bus == 'usb':
  620.                                     idd = serial
  621.                                 elif bus == 'net':
  622.                                     idd = host
  623.                                 elif bus == 'par':
  624.                                     idd = dev_file
  625.                                 else:
  626.                                     idd = 'unknown'
  627.  
  628.                                 self.model = models.normalizeModelUIName(model)
  629.  
  630.                                 if back_end == 'hp':
  631.                                     d = self.__tr("%1 Printer (%2)").arg(model).arg(idd)
  632.  
  633.                                 elif back_end == 'hpaio':
  634.                                     d = self.__tr("%1 Scanner (%2)").arg(model).arg(idd)
  635.  
  636.                                 elif back_end == 'hpfax':
  637.                                     d = self.__tr("%1 Fax (%2)").arg(model).arg(idd)
  638.  
  639.                                 else:
  640.                                     d = self.__tr("%1 (%2)").arg(model).arg(idd)
  641.  
  642.                             if show_message:
  643.                                 if have_pynotify and pynotify.init("hplip"): # Use libnotify/pynotify
  644.                                     icon, urgency = ERROR_STATE_TO_ICON_AND_URGENCY_PYNOTIFY.get(error_state,
  645.                                         (getPynotifyIcon('info'), pynotify.URGENCY_NORMAL))
  646.  
  647.                                     if event.job_id and event.title:
  648.                                         msg = "%s\n%s: %s\n(%s/%s)" % (unicode(d), desc, event.title, event.username, event.job_id)
  649.                                         log.debug("Notify: uri=%s desc=%s title=%s user=%s job_id=%d code=%d" %
  650.                                                 (event.device_uri, desc, event.title, event.username, event.job_id, event.event_code))
  651.                                     else:
  652.                                         msg = "%s\n%s (%s)" % (unicode(d), desc, event.event_code)
  653.                                         log.debug("Notify: uri=%s desc=%s code=%d" % (event.device_uri, desc, event.event_code))
  654.  
  655.                                     n = pynotify.Notification("HPLIP Device Status", msg, icon)
  656.                                     n.set_urgency(urgency)
  657.  
  658.                                     if error_state == ERROR_STATE_ERROR:
  659.                                         n.set_timeout(pynotify.EXPIRES_NEVER)
  660.                                     else:
  661.                                         n.set_timeout(TRAY_MESSAGE_DELAY)
  662.  
  663.                                     n.show()
  664.  
  665.                                 else: # Use "standard" message bubbles
  666.                                     icon = ERROR_STATE_TO_ICON.get(error_state, QSystemTrayIcon.Information)
  667.                                     if event.job_id and event.title:
  668.                                         log.debug("Bubble: uri=%s desc=%s title=%s user=%s job_id=%d code=%d" %
  669.                                                 (event.device_uri, desc, event.title, event.username, event.job_id, event.event_code))
  670.                                         self.tray_icon.showMessage(self.__tr("HPLIP Device Status"),
  671.                                             QString("%1\n%2: %3\n(%4/%5)").\
  672.                                             arg(d).\
  673.                                             arg(desc).arg(event.title).\
  674.                                             arg(event.username).arg(event.job_id),
  675.                                             icon, TRAY_MESSAGE_DELAY)
  676.  
  677.                                     else:
  678.                                         log.debug("Bubble: uri=%s desc=%s code=%d" % (event.device_uri, desc, event.event_code))
  679.                                         self.tray_icon.showMessage(self.__tr("HPLIP Device Status"),
  680.                                             QString("%1\n%2 (%3)").arg(d).\
  681.                                             arg(desc).arg(event.event_code),
  682.                                             icon, TRAY_MESSAGE_DELAY)
  683.  
  684.             else:
  685.                 break
  686.  
  687.  
  688.     def blipTimeout(self):
  689.         if self.active_icon:
  690.             self.tray_icon.setIcon(self.prop_icon)
  691.             self.active_icon = False
  692.  
  693.  
  694.  
  695.     def __tr(self, s, c=None):
  696.         return QApplication.translate("SystemTray", s, c, QApplication.UnicodeUTF8)
  697.  
  698.  
  699.  
  700. def run(read_pipe):
  701.     log.set_module("hp-systray(qt4)")
  702.     log.debug("PID=%d" % os.getpid())
  703.  
  704.     app = SystemTrayApp(sys.argv, read_pipe)
  705.     app.setQuitOnLastWindowClosed(False) # If not set, settings dlg closes app
  706.  
  707.     i = 0
  708.     while i < 10:
  709.         if QSystemTrayIcon.isSystemTrayAvailable():
  710.             break
  711.         time.sleep(1.0)
  712.         i += 1
  713.  
  714.     if not QSystemTrayIcon.isSystemTrayAvailable():
  715.         FailureUI(None,
  716.             QApplication.translate("SystemTray",
  717.             "<b>No system tray detected on this system.</b><p>Unable to start, exiting.</p>",
  718.             None, QApplication.UnicodeUTF8),
  719.             QApplication.translate("SystemTray", "HPLIP Status Service",
  720.             None, QApplication.UnicodeUTF8))
  721.     else:
  722.         notifier = QSocketNotifier(read_pipe, QSocketNotifier.Read)
  723.         QObject.connect(notifier, SIGNAL("activated(int)"), app.notifierActivated)
  724.  
  725.         app.exec_()
  726.  
  727.  
  728.